#include "rpc/net/socket.h"
#include "rpc/net/socket_opt.h"

#include <netinet/in.h>

#define DEFAULT_IP "0.0.0.0"

namespace rpc {
Addr::Addr()
    : ip(DEFAULT_IP), port(0) {
  ::memset(&addr, 0, sizeof(addr));
}

Addr::Addr(const std::string&ip, uint16 port)
    : ip(ip), port(port) {
  ::memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);

  if (empty()) {
    addr.sin_addr.s_addr = INADDR_ANY;
    return;
  }
  addr.sin_addr.s_addr = StrToIp(ip);
}

const std::string Addr::DebugString() const {
  char buf[256] = { '\0' };
  snprintf(buf, sizeof(buf), "%s: %d", (ip.empty() ? DEFAULT_IP : ip), port);
  return std::string(buf);
}

Socket::Socket()
    : fd_(sock::CreateSocket()) {
  CHECK(fd_ != -1);
}

Socket::~Socket() {
  sock::Close(fd_);
}

bool Socket::Listen(const Addr& addr) {
  if (!sock::Bind(this, addr) || !sock::Listen(this)) {
    return false;
  }

  local_addr_ = addr.empty() ? sock::GetSockAddr(this, true) : addr;
  return true;
}

Socket* Socket::Accept(Addr* addr) {
  int32 fd = sock::Accept(this, addr);
  if (fd == -1) return NULL;

  Socket* s = Socket::CreateSocket(fd);
  if (addr == NULL || addr->empty()) {
    s->SetLocalAddr(sock::GetSockAddr(s, true));
  } else {
    s->SetLocalAddr(*addr);
  }

  return s;
}

bool Socket::Connect(const Addr& addr) {
  return sock::Connect(this, addr);
}

std::string Socket::DebugString() const {
  if (fd_ == -1) {
    return "";
  }

  std::stringstream ss;
  ss << "file handle: " << fd_ << std::endl << "local addr: "
     << local_addr_.DebugString() << std::endl << "remote addr: "
     << remote_addr_.DebugString();

  return ss.str();
}

}
